#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <net/if.h>
#include <pcre.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include "hiredis.h"
#include "decs.h"

#ifndef BOOL
#define BOOL int
#endif

#ifndef TRUE
#define TRUE 1
#endif

#ifndef FALSE
#define FALSE 0
#endif

ngx_int_t
dec_making_add_listening(ngx_conf_t *cf, decs_cfg_t *cfg, decs_t *d);
BOOL decs_check_ext(req_t *r);
char* decs_check_policy_domain(ngx_pool_t *pool, req_t *r, policy_res_t *res,  int usesudo);


char* decs_var_exp(req_t *r, char *varexp)
{
    char *dststr = (char*)calloc(1500, 1);
    char *c = varexp;
    char *d = dststr;
    while(*c != '\0')
    {
        if (*c=='(' && *(c+1)=='$')
        {
            char *p = c+1;
            while(*p && *p!=')')
            {
                p++;
            }
            if (*p == ')')
            {
                char *name;
                char *v = NULL;

                name = strndup(c+2, p-c-2);
                /* use name catch substring fill in v */
                pcre_get_named_substring(r->re_url, (char*)r->uri.data, r->vector, r->rc, name, (const char **)&v);
                free(name);
                if (v)
                {
                    size_t len = strlen(v);
		      if (d+len <dststr +1500)
		      {
                    		strncpy(d, v, len);
                    		d = d + len;
                     }
                    		c= p + 1;
                     free(v);
                }
                else
                {
		      if ((d + (p -c + 1)) < (dststr +1500))
		      {
	                    strncpy(d, c, p-c+1);
	                    d = d + (p-c+1);
		      }
                    c = p + 1;
                }
            } else
            {
                strcpy(d, c);
                d = d+(p-c);
                c = p;
            }
        }
        else
            *d++ = *c++;
    }
    return dststr;
}

void decs_log_send(req_t *r, ngx_str_t *policy_name)
{
	size_t ret;
    ngx_str_t nstr = {strlen("posted"), (u_char*)("posted")};
    char *pattern = "{\"domain\":\"%V\", \"policy\":\"%V\"}";
    if (decs_cfg->log_sock)
    {
        char buf[5000];
        ngx_sprintf((u_char*)buf, "{\"domain\":\"%V\", \"policy\":\"%V\"}", &r->host, policy_name ? policy_name : &nstr);
        ret = sendto(decs_cfg->log_sock, buf, strlen(pattern) + r->host.len + (policy_name ? policy_name->len : nstr.len) - 4, 0,
            (struct sockaddr*)&decs_cfg->log_server_addr, sizeof(decs_cfg->log_server_addr));
    }
}

BOOL find_domain_cfg(ngx_str_t *host, dm_t *dm)
{
    redisReply *reply;    
    u_char     *p;
    
    reply = redis_get("dm", host->data, host->len);
    if (reply)
    {
        memcpy(dm, reply->str, sizeof(dm_t));
        freeReplyObject(reply);
        return TRUE;
    }

    p = ngx_strlchr(host->data, host->data + host->len, '.');
    if (!p) return FALSE;

    reply = redis_get("dm", p, host->data + host->len - p);
    if (reply)
    {
        memcpy(dm, reply->str, sizeof(dm_t));
        freeReplyObject(reply);
        return TRUE;
    }
    
    return FALSE;
}

int decs_check_user(ngx_str_t *host, ngx_str_t *cookie, user_t *u)
{
    redisReply *reply;    

    reply = redis_get_cookie(host, cookie);
    if (!reply) return -1;
    memcpy(u, reply->str, sizeof(user_t));
    freeReplyObject(reply);

    if (u->id[0] == '\0') //ûӳ���
    {
        return -2;
    }

    if(0 == redis_get_user(u))
    {
        return 0;
    }

    return 1;
}

long checksum(unsigned short *addr, unsigned int count) {
          /* Compute Internet Checksum for "count" bytes
            *         beginning at location "addr".
            */
       register long sum = 0;


        while( count > 1 )  {
           /*  This is the inner loop */
               sum += * addr++;
               count -= 2;
       }
           /*  Add left-over byte, if any */
       if( count > 0 )
               sum += * (unsigned char *) addr;

           /*  Fold 32-bit sum to 16 bits */
       while (sum>>16)
           sum = (sum & 0xffff) + (sum >> 16);

       return ~sum;
}

long get_tcp_checksum(struct iphdr * myip, struct tcphdr * mytcp, int ip_tot_len) {
    
        struct tcp_pseudo /*the tcp pseudo header*/
        {
          uint32_t src_addr;
          uint32_t dst_addr;
          uint8_t zero;
          uint8_t proto;
          uint16_t length;
        } pseudohead;

        uint16_t total_len = ip_tot_len;

        int tcpopt_len = mytcp->doff*4 - 20;
        int tcpdatalen = total_len - (mytcp->doff*4) - (myip->ihl*4);

        pseudohead.src_addr=myip->saddr;
        pseudohead.dst_addr=myip->daddr;
        pseudohead.zero=0;
        pseudohead.proto=IPPROTO_TCP;
        pseudohead.length=htons(sizeof(struct tcphdr) + tcpopt_len + tcpdatalen);

        int totaltcp_len = sizeof(struct tcp_pseudo) + sizeof(struct tcphdr) + tcpopt_len + tcpdatalen;
        unsigned short tcp[1600];


        memcpy((unsigned char *)tcp,&pseudohead,sizeof(struct tcp_pseudo));
        memcpy((unsigned char *)tcp+sizeof(struct tcp_pseudo),(unsigned char *)mytcp,sizeof(struct tcphdr));
        memcpy((unsigned char *)tcp+sizeof(struct tcp_pseudo)+sizeof(struct tcphdr), (unsigned char *)myip+(myip->ihl*4)+(sizeof(struct tcphdr)), tcpopt_len);
        memcpy((unsigned char *)tcp+sizeof(struct tcp_pseudo)+sizeof(struct tcphdr)+tcpopt_len, (unsigned char *)mytcp+(mytcp->doff*4), tcpdatalen);

/*      printf("pseud length: %d\n",pseudohead.length);
        printf("tcp hdr length: %d\n",mytcp->doff*4);
        printf("tcp hdr struct length: %d\n",sizeof(struct tcphdr));
        printf("tcp opt length: %d\n",tcpopt_len);
        printf("tcp total+psuedo length: %d\n",totaltcp_len);

        fflush(stdout);

        printf("tcp data len: %d, data start %u\n", tcpdatalen,mytcp + (mytcp->doff*4));
*/


         return checksum(tcp,totaltcp_len);

}

int fast_send_http_response(int sock, struct iphdr *req_ip_hdr, struct iphdr *res_ip_hdr, size_t res_ip_len)
{
    struct tcphdr *req_tcph;
    struct tcphdr *res_tcph;
    int req_data_len;
    struct sockaddr_in addr;
    
    res_ip_hdr->version = 4;
    res_ip_hdr->ihl = (sizeof(struct iphdr) >> 2);
    res_ip_hdr->tos = 0;
    res_ip_hdr->tot_len = 0;
    res_ip_hdr->id = 0;
    res_ip_hdr->frag_off = 0;
    res_ip_hdr->ttl = 100;
    res_ip_hdr->protocol = IPPROTO_TCP;
    res_ip_hdr->check = 0; 
    res_ip_hdr->saddr = req_ip_hdr->daddr;
    res_ip_hdr->daddr = req_ip_hdr->saddr;

    req_tcph = (struct tcphdr*)((u_char*)req_ip_hdr + (req_ip_hdr->ihl << 2));
    res_tcph = (struct tcphdr*)((u_char*)res_ip_hdr + sizeof(struct iphdr));
    
    memset(res_tcph, 0, sizeof(*res_tcph));
	res_tcph->source = req_tcph->dest;
	res_tcph->dest = req_tcph->source;
	res_tcph->seq = req_tcph->ack_seq;
    req_data_len = ntohs(req_ip_hdr->tot_len) 
        - (req_ip_hdr->ihl << 2) - (req_tcph->doff << 2);
	res_tcph->ack_seq = htonl(ntohl(req_tcph->seq) + req_data_len);
	res_tcph->doff = (sizeof(struct tcphdr) >> 2);
	res_tcph->fin = 1;
	res_tcph->ack = 1;
	res_tcph->window = 0x4200;
	res_tcph->check = (u_int16_t)get_tcp_checksum(res_ip_hdr, res_tcph, res_ip_len);

    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = res_ip_hdr->daddr;
    addr.sin_port = res_tcph->dest;
    return sendto(sock, res_ip_hdr, res_ip_len, 0, (struct sockaddr*)&addr, sizeof(addr));
}

BOOL parse_pkt(u_char *http, u_char *end, req_t *r)
{
    u_char *p;
    u_char *t;
    u_char *h;

    u_char      *slash = NULL;
    u_char      *dot = NULL;
    
    r->ad_id.data = NULL;
    r->uri.data = NULL;
    r->host.data = NULL;
    r->cookie.data = NULL;
    r->ua.data = NULL;
    r->ext.data = NULL;
    
    p = http;
    while (p < end && *p != ' ') p++;
    if (p >= end) return FALSE;
    while (p < end && *p == ' ') p++;
    if (p >= end) return FALSE;
    t = p;
    while (p < end && *p != ' ') p++;
    if (p >= end) return FALSE;
    r->uri.data = t;
    r->uri.len = p - t;

    h = t;
    if ((*h == 'h' || *h == 'H') &&
    	(*(h+1) == 't' || *(h+1) == 'T') &&
    	(*(h+2) == 't' || *(h+2) == 'T') &&
    	(*(h+3) == 'p' || *(h+3) == 'P') &&
    	*(h+4) == ':' &&
	*(h+5) == '/' &&
	*(h+6) == '/' )
	{
		h = h+7;
		while (h < end && (*h != ' ' && *h != '/')) h++;
		r->uri.data = h;
		r->uri.len = p-h;
	}
    //ext
    for (p = r->uri.data; p < r->uri.data + r->uri.len; p++)
    {
        if (*p == '/') slash = p;
        else if (*p == '.') dot = p;
        else if (*p == '?') break;
    }
    if (dot && slash && dot > slash)
    {
        r->ext.data = dot + 1;
        r->ext.len = p - r->ext.data;
    }

    p = ngx_strlcasestrn(http, end, (u_char*)"host:", 4);
    if (p)
    {
        p += 5;
        while (p < end && *p == ' ') p++;
        if (p >= end) return FALSE;
        t = p;
        while (p < end && *p != '\r') p++;
        if (p >= end) return FALSE;
        r->host.data = t;
        r->host.len = p - t;
    }
    
    p = ngx_strlcasestrn(http, end, (u_char*)"cookie:", 6);
    if (p)
    {
        p += 7;
        while (p < end && *p == ' ') p++;
        if (p >= end) return FALSE;
        t = p;
        while (p < end && *p != '\r') p++;
        if (p >= end) return FALSE;
        r->cookie.data = t;
        r->cookie.len = p - t;
    }
    
    p = ngx_strlcasestrn(http, end, (u_char*)"user-agent:", 10);
    if (p)
    {
        p += 11;
        while (p < end && *p == ' ') p++;
        if (p >= end) return FALSE;
        t = p;
        while (p < end && *p != '\r') p++;
        if (p >= end) return FALSE;
        r->ua.data = t;
        r->ua.len = p - t;
    }
    
    return TRUE;
}

BOOL parse_pkt_lb(u_char *http, u_char *end, req_t *r)
{
    u_char *p;
    u_char *t;
    int bad_pkt = 0;
    size_t len;
    u_char      *slash = NULL;
    u_char      *dot = NULL;

    r->ad_id.data = NULL;
    r->uri.data = NULL;
    r->host.data = NULL;
    r->cookie.data = NULL;
    r->ua.data = NULL;
    r->ext.data = NULL;

    p = http;
    len = strnlen((char*)p, end - p);
    if (len)
    {
        r->uri.data = p;
        r->uri.len = len;
        p += len;

        //ext
        for (t = r->uri.data; t < r->uri.data + r->uri.len; t++)
        {
            if (*t == '/') slash = t;
            else if (*t == '.') dot = t;
            else if (*t == '?') break;
        }
        if (dot && slash && dot > slash)
        {
            r->ext.data = dot + 1;
            r->ext.len = t - r->ext.data;
        }
    }
    else
        bad_pkt = 1;

    p++;
    if (p >= end) return FALSE;

    len = strnlen((char*)p, end - p);
    if (len)
    {
        r->host.data = p;
        r->host.len = len;
        p += len;
    }
    else
        bad_pkt = 1;

    p++;
    if (p >= end) return FALSE;

    len = strnlen((char*)p, end - p);
    if (len)
    {
        r->cookie.data = p;
        r->cookie.len = len;
        p += len;
    }
    else
        bad_pkt = 1;

    p++;
    if (p >= end) return FALSE;

    len = strnlen((char*)p, end - p);
    if (len)
    {
        r->ua.data = p;
        r->ua.len = len;
    }

    return !bad_pkt;
}

#if 0
BOOL select_ad(req_t *r)
{
    redisReply *reply;
    time_t        t;
    struct tm     lt;
    redis_t      *redis;
    
    if (r->dm.ad_id == 0)
    {
        decs_log_debug("req_%08xd domain is only for cookie-mapping", r->id);
        return FALSE;
    }
    
    reply = redis_get("ad", &r->dm.ad_id, sizeof(r->dm.ad_id));
    if (!reply)
    {
        decs_log_error("req_%08xd ad %d not found", r->id, r->dm.ad_id);
        return FALSE;
    }
    memcpy(&r->ad, reply->str, sizeof(ad_t));
    freeReplyObject(reply);

    //���Ͷ��ʱ��
    time(&t);
    localtime_r(&t, &lt);
    if ((r->ad.time_bits & (1 << lt.tm_hour)) == 0)
    {
        decs_log_debug("req_%08xd not in the right time", r->id);
        return FALSE;
    }

    //���Ͷ�ż��
    redis = &decs_cfg->redis;
    reply = redis_cmd(redis, "SET adlog-%b-%d '' EX %d NX", 
        &r->u.id, sizeof(r->u.id), r->dm.ad_id, r->dm.ad_interval);
    if (!reply)
    {
        decs_log_error("req_%08xd failed to set adlog in redis", r->id);
        return FALSE;
    }
    if (reply->type == REDIS_REPLY_NIL)
    {
        decs_log_debug("req_%08xd ad already posted", r->id);
        return FALSE;
    }
    freeReplyObject(reply);
    
    return TRUE;
}
#endif

void create_empty_user(req_t *r)
{
    redisReply *reply;    
    user_t u;
    ngx_str_t *host = &r->host;
    ngx_str_t *cookie = &r->cookie;
    redis_t *redis;
    
    redis = &decs_cfg->redis;

    memset(&u, 0, sizeof(u));
    reply = redis_cmd(redis, "SET cookie-%b-%b %b EX %d", 
        host->data, host->len, cookie->data, cookie->len,
        &u, sizeof(u), decs_cfg->cookie_map_interval);
    if (!reply)
    {
        decs_log_error("req_%08xd failed to set cookie in redis", r->id);
        return;
    }
    if (strcmp(reply->str, "OK") != 0)
    {
        decs_log_error("req_%08xd failed to set cookie in redis, reply: %s", r->id, reply->str);
    }
    else
    {
        decs_log_debug("req_%08xd empty user created", r->id);
    }
    freeReplyObject(reply);
}

unsigned char ToHex(unsigned char x)   
{   
    return  x > 9 ? x + 55 : x + 48;   
}

int UrlEncode(char *src, int src_len, char *dst, int size)
{  
    char *p;
    int i;
    
    p = dst;
    
    for (i = 0; i < src_len; i++)  
    {  
        if (isalnum(src[i]) ||   
            (src[i] == '-') ||  
            (src[i] == '_') ||   
            (src[i] == '.') ||   
            (src[i] == '~'))  
        {
            if (p + 1 - dst >= size) return -1;
            *(p++) = src[i];  
        }
        else if (src[i] == ' ')  
        {
            if (p + 1 - dst >= size) return -1;
            *(p++) = '+';  
        }
        else  
        {
            if (p + 3 - dst >= size) return -1;
            *(p++) = '%';  
            *(p++) = ToHex((unsigned char)src[i] >> 4);  
            *(p++) = ToHex((unsigned char)src[i] % 16);  
        }  
    }
    *p = '\0';
    return 0;  
}  

void decs_log_access(req_t *r, char *code)
{
    u_char  s[2000];
    char    ip[16] = "";
    u_char  *p;

    if (decs_cfg->access_log == NULL)
    {
        return;
    }
    
    inet_ntop(AF_INET, &r->iph->saddr, ip, sizeof(ip));
    if (r->ua.data == NULL)
    {
        ngx_str_set(&r->ua, "");
    }
    if (r->cookie.data == NULL)
    {
        ngx_str_set(&r->cookie, "");
    }
    
    //TIME, CODE, IP, DOMAIN, COOKIE, UA
    p = ngx_sprintf(s, "%T|%s|%s|%V|%V|%V\n", ngx_time(), code, ip, &r->host, &r->cookie, &r->ua);
    
    ngx_write_fd(decs_cfg->access_log->fd, s, p - s);
}

void cookie_mapping(req_t *r)
{
    u_char s[1500+5000];
    u_char *p;
    int content_length;
    char host_encoded[256];
    char cookie_encoded[5000];
    char sep;

    if (!decs_cfg->cookie_iframe)
    {
        decs_log_error("req_%08xd set-cookie iframe template not found", r->id);
        return;
    }
    
    if (0 != UrlEncode((char*)r->host.data, r->host.len, host_encoded, sizeof(host_encoded))) return;
    if (0 != UrlEncode((char*)r->cookie.data, r->cookie.len, cookie_encoded, sizeof(cookie_encoded))) return;

    if (decs_cfg->url_tag.data)
    {
        if (ngx_strlchr(r->uri.data, r->uri.data + r->uri.len, '?'))
            sep = '&';
        else
            sep = '?';
        
        content_length = strlen(decs_cfg->cookie_iframe) - 14 + r->host.len + r->uri.len + 1
            + decs_cfg->url_tag.len
            + strlen(host_encoded) + strlen(cookie_encoded) + decs_cfg->area.len;
    }
    else
    {
        content_length = strlen(decs_cfg->cookie_iframe) - 10 + r->host.len + r->uri.len
            + strlen(host_encoded) + strlen(cookie_encoded) + decs_cfg->area.len;
    }
    
    //Ԥ��IP/TCPͷ��
    p = s + sizeof(struct iphdr) + sizeof(struct tcphdr);
    p = ngx_sprintf(p, 
        "HTTP/1.1 200 OK\r\n"
        "Connection: close\r\n"
        "Content-Type: text/html\r\n"
        "Content-Length: %d\r\n"
        "Cache-Control: no-cache\r\n"
        "\r\n", (ngx_int_t)content_length);
    if (decs_cfg->url_tag.data)
    {
        p = ngx_slprintf(p, s + sizeof(s), decs_cfg->cookie_iframe, &r->host, &r->uri, sep, decs_cfg->url_tag.data,
            host_encoded, cookie_encoded, decs_cfg->area.data);
    }
    else
    {
        p = ngx_slprintf(p, s + sizeof(s), decs_cfg->cookie_iframe, &r->host, &r->uri,
            host_encoded, cookie_encoded, decs_cfg->area.data);
    }
    
    if (fast_send_http_response(decs_cfg->raw_sock, r->iph, (struct iphdr *)s, p - s) < 0)
    {
        decs_log_error("req_%08xd fast_send, errno: %d", r->id, errno);
    }
    else
    {
        decs_log_debug("req_%08xd cookie mapping sent", r->id);
        decs_log_access(r, "SETCOOKIE");
    }
}

void url_redirect(req_t *r)
{
    u_char s[1500+5000];
    u_char *p;
    u_char *tmp;
    
    //Ԥ��IP/TCPͷ��
    p = s + sizeof(struct iphdr) + sizeof(struct tcphdr);
 /*   p = ngx_sprintf(p, 
        "HTTP/1.1 302 Found\r\n"
        "Connection: close\r\n"
        "Location: %s\r\n"
        "Content-Type: text/html\r\n"
        "Content-Length: 0\r\n"
        "Cache-Control: no-cache\r\n"
        "\r\n",r->url.data); */

    tmp = (u_char*)decs_var_exp(r, (char*)r->url.data);
    p = ngx_sprintf(p, 
        "HTTP/1.1 302 Found\r\n"
        "Connection: close\r\n"
        "Location: %s\r\n"
        "Content-Type: text/html\r\n"
        "Content-Length: 0\r\n"
        "Cache-Control: no-cache\r\n"
        "\r\n", tmp);

    if (fast_send_http_response(decs_cfg->raw_sock, r->iph, (struct iphdr *)s, p - s) < 0)
    {
        decs_log_error("req_%08xd fast_send, errno: %d", r->id, errno);
    }
    else
    {
        decs_log_debug("req_%08xd redirect msg sent", r->id);
        decs_log_access(r, "REDIRECT");
    }
}

char* judge_policy(decs_cfg_t *cfg, req_t *r, policy_res_t *res)
{
    char *ret;
    ret = decs_check_policy_domain(cfg->pool, r, res, 0);   /* compare host */
    if  (ret) return ret;
    return decs_check_policy_domain(cfg->pool, r, res, 1);  /* compare without host */
}

void post_ad(req_t *r)
{
    u_char s[1500] = {0};
    u_char *p;
    int content_length;
    char *iframe = decs_cfg->ad_iframe;
    char sep;
    int click = 0;
	policy_res_t res={0, NULL};
    
    if (!iframe)
    {
        decs_log_error("req_%08xd iframe template not found for position %d", r->id,r->pos);
        return;
    }

    if (decs_cfg->url_tag.data)
    {
    	//添加策略判断
    	ngx_str_t ignore = {strlen("ignore"), (u_char*)"ignore"};
    	char *frame = judge_policy(decs_cfg, r, &res);
    	if (!frame) decs_log_send(r, &ignore);
    	/* 如果是URL重定向 */
    	if (res.type == POLICY_RES_REDIR)
    	{
    		r->url.data = (u_char*)res.content;
    		url_redirect(r);
    		return;
    	}
    	if (frame)
    		iframe = frame;
	else
		return;

        if (ngx_strlchr(r->uri.data, r->uri.data + r->uri.len, '?'))
            sep = '&';
        else
            sep = '?';


        content_length = strlen(iframe) - 16 + r->host.len + r->uri.len + 1 + decs_cfg->url_tag.len
        + r->host.len + decs_cfg->area.len + 1 + r->ad_id.len;
    }
    else
    {
        content_length = strlen(iframe) - 14 + r->host.len +r->uri.len + 
        r->host.len + decs_cfg->area.len + r->host.len + decs_cfg->area.len + 1;
    }

    if (decs_cfg->click_rate && rand() % 100 < decs_cfg->click_rate)
    {
        click = 1;
    }
    
    //Ԥ��IP/TCPͷ��
    p = s + sizeof(struct iphdr) + sizeof(struct tcphdr);
    p = ngx_sprintf(p, 
        "HTTP/1.1 200 OK\r\n"
        "Connection: closed\r\n"
        "Content-Type: text/html\r\n"
        "Content-Length: %d\r\n"
        "Cache-Control: no-cache\r\n"
        "\r\n", (ngx_int_t)content_length);
    if (decs_cfg->url_tag.data)
    {

        p = ngx_slprintf(p, s + sizeof(s), iframe, &r->host, &r->uri, sep, 
        decs_cfg->url_tag.data, click, &r->host, &decs_cfg->area, &r->ad_id);
    }
    else
    {
        p = ngx_slprintf(p, s + sizeof(s), iframe, &r->host, 
        &r->uri,&r->host,decs_cfg->area.data,&r->host,decs_cfg->area.data,r->pos);
    }
    
    if (fast_send_http_response(decs_cfg->raw_sock, r->iph, (struct iphdr *)s, p - s) < 0)
    {
        decs_log_error("req_%08xd fast_send failed, errno: %d", r->id, errno);
    }
    else
    {
        decs_log_debug("req_%08xd ad posted", r->id);
        decs_log_access(r, "AD");
    }
}

BOOL is_local_ip(char *ip)
{
	int  MAXINTERFACES=16;
	int fd, intrface;
	struct ifreq buf[MAXINTERFACES];
	struct ifconf ifc;
	char *local_ip;
    
    fd = socket (AF_INET, SOCK_DGRAM, 0);
    if (fd == -1) return FALSE;
    
	ifc.ifc_len = sizeof buf;
	ifc.ifc_buf = (caddr_t) buf;
	if (ioctl (fd, SIOCGIFCONF, (char *) &ifc) == -1)
	{
        close(fd);
        return FALSE;
	}
	close (fd);
        
	intrface = ifc.ifc_len / sizeof (struct ifreq); 
	while (intrface-- > 0)
	{
    	local_ip = inet_ntoa(((struct sockaddr_in*)(&buf[intrface].ifr_addr))->sin_addr);
        if (strcmp(local_ip, ip) == 0)
        {
            return TRUE;
        }
	}
    return FALSE;
}

static void
decs_rbtree_insert_value(ngx_rbtree_node_t *temp,
    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
{
    ngx_rbtree_node_t       **p;
    decs_rbtree_node_t    *cntnode, *cntnode_temp;

    for ( ;; ) {

        if (node->key < temp->key) {

            p = &temp->left;

        } else if (node->key > temp->key) {

            p = &temp->right;

        } else { /* node->key == temp->key */

        	cntnode = (decs_rbtree_node_t *) node;
        	cntnode_temp = (decs_rbtree_node_t *) temp;

            p = (ngx_strcmp(cntnode->key, cntnode_temp->key) < 0)
                    ? &temp->left : &temp->right;
        }

        if (*p == sentinel) {
            break;
        }

        temp = *p;
    }

    *p = node;
    node->parent = temp;
    node->left = sentinel;
    node->right = sentinel;
    ngx_rbt_red(node);
}

static decs_rbtree_node_t *
decs_rbtree_lookup(decs_rbtree_sh_t *head, char *key,
    uint32_t hash)
{
    ngx_int_t                rc;
    ngx_rbtree_node_t       *node, *sentinel;
    decs_rbtree_node_t  *decs_node;

    node = head->rbtree.root;
    sentinel = head->rbtree.sentinel;

    while (node != sentinel) {

        if (hash < node->key) {
            node = node->left;
            continue;
        }

        if (hash > node->key) {
            node = node->right;
            continue;
        }

        /* hash == node->key */

        decs_node = (decs_rbtree_node_t *) node;

        rc = strcmp(key, decs_node->key);

        if (rc == 0) {
            return decs_node;
        }

        node = (rc < 0) ? node->left : node->right;
    }

    return NULL;
}

BOOL decs_check_node(u_char *key, size_t len, int max_cnt)
{
    uint32_t hash;
    decs_rbtree_node_t *node;
    
    hash = ngx_crc32_long(key, len);
    
    ngx_shmtx_lock(&decs_cfg->shpool->mutex);

    node = decs_rbtree_lookup(decs_cfg->sh, (char *)key, hash);
    if (node)
    {
        if (node->count < max_cnt)
        {
            node->count++;
            ngx_shmtx_unlock(&decs_cfg->shpool->mutex);
            return TRUE;
        }
        else
        {
            ngx_shmtx_unlock(&decs_cfg->shpool->mutex);
            return FALSE;
        }
    }
    else
    {
        node = ngx_slab_alloc_locked(decs_cfg->shpool,
                                    sizeof(decs_rbtree_node_t));
        if (!node)
        {
            ngx_shmtx_unlock(&decs_cfg->shpool->mutex);
            return FALSE;
        }
        node->key = ngx_slab_alloc_locked(decs_cfg->shpool, len + 1);
        if (!node->key)
        {
            ngx_slab_free_locked(decs_cfg->shpool, node);
            ngx_shmtx_unlock(&decs_cfg->shpool->mutex);
            return FALSE;
        }
        strcpy(node->key, (char*)key);

        node->count = 1;
        node->node.key = hash;
        ngx_rbtree_insert(&decs_cfg->sh->rbtree, &node->node);
        ngx_shmtx_unlock(&decs_cfg->shpool->mutex);
        return TRUE;
    }
}

char* decs_check_policy_domain(ngx_pool_t *pool, req_t *r, policy_res_t *res, int usesudo)
{
    dm_t *dm = NULL;
    dm_policy_t **dmp_ptr;
    dm_policy_t *dmp;
    //const char      *s = NULL;
    ngx_str_t *host = NULL;
    ngx_str_t *cookie = NULL;
    char ip[16];
    u_char key[3000];
    ngx_str_t sudohost={(size_t)7,(u_char*)"0.0.0.0"};
    int i;
    
    ngx_strlow(r->host.data, r->host.data, r->host.len);
    if (usesudo) {
        host= &sudohost;
    }else{
        host = &r->host; 
    }
    cookie = &r->cookie;


    if (decs_cfg->policy_domain_hash.buckets)
    {
    	dm_policy_t *empty_dmp[100];
    	int empty_dmp_no = 0;

    	/* 先按域名找到hash节点 */
    	dmp_ptr = (dm_policy_t**)ngx_hash_find_ptr(&decs_cfg->policy_domain_hash,
            ngx_hash_key_lc(host->data, host->len),
			host->data, host->len);

    	/* 遍历hash节点，找出匹配的策略 */
        if (dmp_ptr)
        {
        	dmp = *dmp_ptr;
        	while(dmp != (void*)-1)
        	{
				dm = dmp->dm;
				if(!dm->ifwhite)
				{
					decs_log_debug("req_%08xd host %V not found while domain", r->id, host);
					decs_log_access(r, "DOMAIN");
					dmp = dmp->next;
					continue;
				}

				r->pos = dm->space;

				/* 先URL判断正则 */
				if (dm->isregular)
				{
                                        r->re_url = dm->re_url;
					r->rc = pcre_exec(dm->re_url, NULL, (char*)r->uri.data, r->uri.len, 0, 0, r->vector, 24);
					if (r->rc < 0)
					{
						decs_log_debug("req_%08xd re_url not match", r->id);
						decs_log_access(r, "URL");
						dmp = dmp->next;
						continue;
					}
				}
				else
				{
					decs_log_debug("req_%08xd re_url not found", r->id);
					if (!decs_check_ext(r))
					{
						decs_log_debug("req_%08xd denied by ext rules", r->id);
						decs_log_access(r, "EXT");
						dmp = dmp->next;
						continue;
					}

				}

				/* 判断UA */
				if (dm->policy->re_ua)
				{
                                        int rc;
                                        int vector[12];
                                        rc = pcre_exec(dm->policy->re_ua, NULL, (char*)r->ua.data,r->ua.len, 0, 0, vector, 12);
					if (rc < 0)
					{
						dmp = dmp->next;
						continue;
					}
				}

        		/* 如果权重为0，则先暂记下来 */
        		if (dmp->weight == 0 )
        		{
        			empty_dmp[empty_dmp_no++] = dmp;
					dmp = dmp->next;
					continue;
        		}
        		else
        		{
        			dmp->weight--;
        		}

                if (dm->policy->per_ip_policy)
                {
                    inet_ntop(AF_INET, &r->iph->saddr, ip, sizeof(ip));
                    ngx_sprintf(key, "%s-%V", ip, &dm->policy->name);
                    if (!decs_check_node(key, strlen((char*)key), dm->policy->per_ip_policy))
                    {
    					dmp = dmp->next;
    					continue;                    
                    }
                }
                else if (dm->policy->per_ip_policy_dm)
                {
                    inet_ntop(AF_INET, &r->iph->saddr, ip, sizeof(ip));
                    ngx_sprintf(key, "%s-%V-%V", ip, &dm->policy->name, &r->host);
                    if (!decs_check_node(key, strlen((char*)key), dm->policy->per_ip_policy_dm))
                    {
    					dmp = dmp->next;
    					continue;                    
                    }
                }
                else if (dm->policy->rate)
                {
            		/* 判断每条策略的投放概率 */
            		if ((unsigned)rand() % 100 > dm->policy->rate)
            		{
    					dmp = dmp->next;
    					continue;
            		}
                }

        		/* 判断是使用模板，还是使用重定向 */
        		if (dm->policy->templatedata)
        		{
        			res->type = POLICY_RES_FRAME;
        			res->content = dm->policy->templatedata;
        		}else
        		{
        			res->type = POLICY_RES_REDIR;
        			res->content = (char*)dm->policy->redirect_url.data;
        		}
                
        		decs_log_send(r, &dm->policy->name);
				return res->content;
        	}

        	for (i = 0; i < empty_dmp_no;i++)
        	{
        		empty_dmp[i]->weight = empty_dmp[i]->dm->policy->weight;
        	}
        }
        else
        {
            decs_log_debug("req_%08xd host %V not found in white domain file", r->id, host);
            decs_log_access(r, "DOMAIN");
            return NULL;
        }
    }

    return NULL;
}

BOOL decs_check_domain(req_t *r)
{
    dm_t *dm = NULL;
    int              vector[12];
    int              rc;
    //const char      *s = NULL;
    ngx_str_t *host = NULL;
    ngx_str_t *cookie = NULL;    

    ngx_strlow(r->host.data, r->host.data, r->host.len);
    host = &r->host;
    cookie = &r->cookie;
    
    if (decs_cfg->cookie_reduce_hash.buckets)
    {
        dm = ngx_hash_find(&decs_cfg->cookie_reduce_hash, 
            ngx_hash_key_lc(host->data, host->len), 
            host->data, host->len);
        if (dm)
        {
            if(!dm->ifwhite)
            {
                decs_log_debug("req_%08xd host %V not found while domain", r->id, host);
                decs_log_access(r, "DOMAIN");
                return FALSE;
            }
            r->pos = dm->space;
            
            if (dm->isregular)
            {
                rc = pcre_exec(dm->re_url, NULL, (char*)r->uri.data,r->uri.len, 0, 0, vector, 12);
                if (rc >= 0)
                {
                    decs_log_debug("req_%08xd re_url matched", r->id);
                }
                else
                {
                    decs_log_debug("req_%08xd re_url not match", r->id);
                    decs_log_access(r, "URL");
                    return FALSE;
                }
            }
            else
            {
                decs_log_debug("req_%08xd re_url not found", r->id);
                if (!decs_check_ext(r))
                {
                    decs_log_debug("req_%08xd denied by ext rules", r->id);
                    decs_log_access(r, "EXT");
                    return FALSE;
                }

            }

            #if 0
            if(dm->iscookie)
            {
                rc = pcre_exec(dm->re_cookie, NULL, (char*)cookie->data, cookie->len, 0, 0, vector, 12);
                if (rc < 0)
                {
                    decs_log_debug("req_%08xd cookie mismatch", r->id);
                    decs_log_access(r, "COOKIE");
                    return FALSE;
                }
                
                rc = pcre_get_substring((char*)cookie->data, vector, rc, 1, &s);
                if (rc < 0)
                {
                    decs_log_error("req_%08xd pcre_get_substring failed", r->id);
                    return FALSE;
                }

                cookie->len = strlen(s);
                memcpy(cookie->data, s, cookie->len);
                pcre_free_substring(s);
                
            }
            #endif
        }
        else
        {
            decs_log_debug("req_%08xd host %V not found in white domain file", r->id, host);
            decs_log_access(r, "DOMAIN");
            return FALSE;
        }
    }
    
    return TRUE;
}

BOOL decs_check_ip(struct iphdr *iph)
{
    ngx_uint_t               i;
    decs_ip_rule_t          *rule;
    in_addr_t                addr;

    if (!decs_cfg->ip_rules) return TRUE;
    
    addr = iph->saddr;
    rule = decs_cfg->ip_rules->elts;
    for (i = 0; i < decs_cfg->ip_rules->nelts; i++) {
        if ((addr & rule[i].mask) == rule[i].addr) 
        {
            if (rule[i].deny)
                return FALSE;
            else
                return TRUE;
        }
    }
    return TRUE;
}

BOOL decs_check_ua(ngx_str_t *ua)
{
    ngx_uint_t               i;
    decs_ua_rule_t          *rule;

    if (!decs_cfg->ua_rules) return TRUE;
    if (!ua->data) return FALSE;
    
    rule = decs_cfg->ua_rules->elts;
    for (i = 0; i < decs_cfg->ua_rules->nelts; i++) 
    {
        if (ngx_strlcasestrn(ua->data, ua->data + ua->len, rule[i].ua.data, 
            rule[i].ua.len - 1) != NULL)
        {
            if (rule[i].deny)
                return FALSE;
            else
                return TRUE;
        }
    }
    return TRUE;
}

BOOL decs_check_redirections(req_t *r)
{
    ngx_uint_t  i;
    int         rc;
    redirect_t  *redirect;
    int         vector[12];
    if (!decs_cfg->redirect) return FALSE; 

    redirect = decs_cfg->redirect->elts;
    for(i = 0;i < decs_cfg->redirect->nelts;i++)
    {
        rc = pcre_exec(redirect[i].re_surl, NULL, (char*)r->url.data,r->url.len, 0, 0, vector, 12);
        if (rc >= 0)
        {
            r->url.data = redirect[i].dst_url.data;
            r->url.len = redirect[i].dst_url.len;
            return TRUE;
        }
    }
    
    return FALSE;
}

BOOL decs_load_balance(req_t *r)
{
    uint32_t   hash;
    ngx_str_t *host = &r->host;
    ngx_str_t *cookie = &r->cookie;
    int        decs_idx;
    decs_t    *d;
    u_char     buf[1500];
    u_char    *p;
    struct tcphdr *tcph;
    u_char *http;
    
    ngx_crc32_init(hash);
    ngx_crc32_update(&hash, host->data, host->len);
    ngx_crc32_update(&hash, cookie->data, cookie->len);
    ngx_crc32_final(hash);
    
    decs_idx = hash % decs_cfg->decs_array->nelts;
    if (decs_idx == decs_cfg->local_decs_idx) return FALSE;

    d = &((decs_t*)decs_cfg->decs_array->elts)[decs_idx];

    tcph = (struct tcphdr*)((u_char*)r->iph + (r->iph->ihl << 2));
    http = (u_char*)tcph + (tcph->doff << 2);

    p = buf;
    *(p++) = 0;
    p = ngx_cpymem(p, r->iph, http - (u_char*)r->iph);
    p = ngx_cpymem(p, r->uri.data, r->uri.len);
    *(p++) = 0;
    p = ngx_cpymem(p, r->host.data, r->host.len);
    *(p++) = 0;
    p = ngx_cpymem(p, r->cookie.data, r->cookie.len);
    *(p++) = 0;
    if (r->ua.data)
        p = ngx_cpymem(p, r->ua.data, r->ua.len);
    *(p++) = 0;
    
    if (sendto(decs_cfg->udp_sock, buf, p - buf, 0, (struct sockaddr*)&d->addr_for_decs, sizeof(d->addr_for_decs)) < 0)
    {
        decs_log_error("req_%08xd sendto failed, errno: %d", r->id, errno);
    }
    else
    {
        decs_log_debug("req_%08xd sent to decs %s", r->id, d->ip);
    }
    return TRUE;
}

BOOL decs_check_ext(req_t *r)
{
    ngx_str_t   *ext_cfg;
    ngx_uint_t   i;

    if (!decs_cfg->url_ext) return TRUE;
    
    if (r->ext.data)
    {
        ext_cfg = decs_cfg->url_ext->elts;
        for (i = 0; i < decs_cfg->url_ext->nelts; i++) 
        {
            if (r->ext.len == ext_cfg[i].len
                && ngx_strncmp(r->ext.data, ext_cfg[i].data, r->ext.len) == 0)
            {
                return TRUE;
            }
        }
    }
    return FALSE;
}

BOOL decs_check_fixed_url_ad(req_t *r)
{
    u_char *s;
    
    if (decs_cfg->fixed_url_ad == NULL) return FALSE;
    
    s = ngx_hash_find(&decs_cfg->fixed_url_ad_hash, 
        ngx_hash_key(r->url.data, r->url.len), 
        r->url.data, r->url.len);
    if (s)
    {
        r->ad_id.data = s;
        r->ad_id.len = ngx_strlen(s);
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

BOOL decs_check_regex_url_ad(req_t *r)
{
    ngx_uint_t i;
    regex_url_ad_t *url_ad;
    int         vector[12];
    int         rc;
    
    if (decs_cfg->regex_url_ad == NULL) return FALSE;
    
    for (i = 0; i < decs_cfg->regex_url_ad->nelts; i++) 
    {
        url_ad = &((regex_url_ad_t*)decs_cfg->regex_url_ad->elts)[i];
        rc = pcre_exec(url_ad->regex, NULL, (char*)r->url.data, r->url.len, 0, 0, vector, 12);
        if (rc >= 0)
        {
            r->ad_id = url_ad->ad_id;
            return TRUE;
        }
        
    }
    
    return FALSE;
}

BOOL decs_log_check_ip(req_t *r)
{
    ngx_uint_t               i;
    decs_ip_rule_t          *rule;
    in_addr_t                addr;

    if (!decs_cfg->log_ip_rules) return TRUE;
    
    addr = r->iph->saddr;
    rule = decs_cfg->log_ip_rules->elts;
    for (i = 0; i < decs_cfg->log_ip_rules->nelts; i++) {
        if ((addr & rule[i].mask) == rule[i].addr) 
        {
            if (rule[i].deny)
                return FALSE;
            else
                return TRUE;
        }
    }
    return TRUE;
}

BOOL decs_log_check_ext(req_t *r)
{
    ngx_str_t   *ext_cfg;
    ngx_uint_t   i;

    if (decs_cfg->log_ext_all) return !decs_cfg->log_ext_deny;
    if (!decs_cfg->log_ext) return !decs_cfg->log_ext_deny;

    if (r->ext.data) 
    {
        ext_cfg = decs_cfg->log_ext->elts;
        for (i = 0; i < decs_cfg->log_ext->nelts; i++) 
        {
            if (r->ext.len == ext_cfg[i].len
                && ngx_strncmp(r->ext.data, ext_cfg[i].data, r->ext.len) == 0)
            {
                return !decs_cfg->log_ext_deny;
            }
        }
    }
    return decs_cfg->log_ext_deny;
}

BOOL decs_log_check_domain(req_t *r)
{
    u_char      *p;
    void        *dm;
    u_char      *host_end = r->host.data + r->host.len;
    
    if (!decs_cfg->log_domain_hash.buckets)
    {
        return TRUE;
    }
    
    dm = ngx_hash_find(&decs_cfg->log_domain_hash, 
        ngx_hash_key_lc(r->host.data, r->host.len), 
        r->host.data, r->host.len);
    if (dm) return TRUE;

    p = r->host.data;
    while (1)
    {
        p = ngx_strlchr(p, host_end, '.');
        if (!p) return FALSE;

        dm = ngx_hash_find(&decs_cfg->log_domain_hash, 
            ngx_hash_key_lc(p, host_end - p), 
            p, host_end - p);
        if (dm) return TRUE;

        p++;
    }
    
    return FALSE;
}

void decs_log_write(req_t *r)
{
    int disk;
    struct timeval tv;
    char SrcIP[16];
    char DstIP[16];
    ngx_open_file_t   *log_file;
    u_char buf[100];
    u_char *p;
    struct tcphdr *tcph;
    u_char *http;
    size_t  http_len;
    struct iovec iov[3];
    int iovcnt = 2;
    ssize_t len;
    ssize_t n;
 
    
    tcph = (struct tcphdr*)((u_char*)r->iph + (r->iph->ihl << 2));
    http = (u_char*)tcph + (tcph->doff << 2);
    http_len = (u_char*)r->iph + ntohs(r->iph->tot_len) - http;
    
    disk = r->id % decs_cfg->log_disk_num;
    log_file = decs_cfg->log_files[disk];
    
    gettimeofday(&tv, NULL);
    
    inet_ntop(AF_INET, &r->iph->saddr, SrcIP, sizeof(SrcIP));
    inet_ntop(AF_INET, &r->iph->daddr, DstIP, sizeof(DstIP));
    
    p = ngx_snprintf(buf, sizeof(buf), "\r\n\r\n\n0 %s %s %d %d %d%03d %d ", 
        SrcIP, DstIP, ntohs(tcph->source), ntohs(tcph->dest), 
        tv.tv_sec, tv.tv_usec / 1000, ntohs(r->iph->tot_len));
    iov[0].iov_base = buf;
    iov[0].iov_len= p - buf;
    iov[1].iov_base = http;
    if (http_len >= 4 && ngx_memcmp(&http[http_len - 4], "\r\n\r\n", 4) == 0)
    {
        iov[1].iov_len= http_len - 4;
    }
    else
    {
        iov[1].iov_len= http_len;
    }

    len = iov[0].iov_len + iov[1].iov_len;
    n = writev(log_file->fd, iov, iovcnt);
    if (n == -1)
    {
        ngx_atomic_fetch_add(&ngx_stat->decs_log_write_fail, 1);
        if (errno != EAGAIN)
        {
            decs_log_error("writev failed, errno %d", errno);
        }
    }
    else if (n != len)
    {
        ngx_atomic_fetch_add(&ngx_stat->decs_log_write_part, 1);
    }
    else
    {
        ngx_atomic_fetch_add(&ngx_stat->decs_log_write_ok, 1);
    }
}



void decs_process_request(req_t *r)
{
    if (decs_cfg->httplog_enabled && decs_log_check_ip(r) && decs_log_check_ext(r) && decs_log_check_domain(r))
    {
        decs_log_write(r);
    }

    if (!decs_cfg->cookie_mapping_enabled && !decs_cfg->ad_enabled)
    {
        decs_log_debug("req_%08xd cookie mapping and ad are not enabled", r->id);
        return;
    }

    #if 0
    if (!r->cookie.data)
    {
        decs_log_debug("req_%08xd packet incomplete", r->id);
        return;
    }
    #endif
    
    if (decs_cfg->url_tag.data && ngx_strlcasestrn(
        r->uri.data, r->uri.data + r->uri.len, 
        decs_cfg->url_tag.data, decs_cfg->url_tag.len - 1) != NULL)
    {
        decs_log_debug("req_%08xd with url_tag, discard", r->id);
        decs_log_send(r, NULL);
        return;
    }

    if (!decs_check_ip(r->iph))
    {
        decs_log_debug("req_%08xd denied by ip rules", r->id);
        decs_log_access(r, "IP");
        return;
    }
    
    if (!decs_check_ua(&r->ua))
    {
        decs_log_debug("req_%08xd denied by ua rules", r->id);
        decs_log_access(r, "UA");
        return;
    }

    r->url.data = r->url_data;
    strncpy((char*)r->url.data,(char*)r->host.data,r->host.len);
    strncpy((char*)r->url.data+r->host.len,(char*)r->uri.data,r->uri.len);
    r->url.len = r->host.len + r->uri.len;

    if(decs_check_redirections(r))
    {   
        decs_log_debug("req_%08xd processed by redirections", r->id);
        url_redirect(r);
        decs_log_access(r, "REDIRECT");
        return;
    }


    if (r->ad_id.data == NULL)
    {
        ngx_str_set(&r->ad_id, "0");
    }
    
#if 0    
    //ngx_strlow(r->cookie.data, r->cookie.data, r->cookie.len);
    if (!find_domain_cfg(&r->host, &r->dm))
    {
        decs_log_debug("req_%08xd host not in cfg", r->id);
        return;
    }

    if (!r->dm.ifwhite)
    {
        decs_log_debug("req_%08xd host not in white list", r->id);
        return;
    }
#endif

    #if 0
    ret = decs_check_user(&r->host, &r->cookie, &r->u);
    if (-1 == ret)
    {
        cookie_mapping(r);
        create_empty_user(r);
        request_sync_data(r);
        return;
    }
    if(-2 == ret)
    {
        decs_log_debug("req_%08xd sync not finished", r->id);
        decs_log_access(r, "SYNC");
        return;
        
    }
    if(1 == ret)
    {
        decs_log_debug("req_%08xd user exit,but not expire", r->id);
        decs_log_access(r, "INTERVAL");
        return;
    }
    if(0 == ret)
    {
        #if 0
        if (r->u.id[0] == '\0') //ûӳ���
        {
            decs_log_debug("req_%08xd sync not finished", r->id);
            return;
        }
        #endif
        
        if (decs_cfg->ad_enabled /*&& select_ad(r)*/)
        {
            post_ad(r);
            return;
        }
    }
    #endif

    if (decs_cfg->show_rate && rand() % 100 < decs_cfg->show_rate)
    {
        post_ad(r);
    }
}

void decs_pkt_handler(char *pkt, int len)
{
    req_t     r;
    struct iphdr *iph = (struct iphdr *)pkt;
    struct tcphdr *tcph;
    u_char *end = (u_char*)pkt + len;
    u_char *http;

    tcph = (struct tcphdr*)((u_char*)iph + (iph->ihl << 2));
    http = (u_char*)tcph + (tcph->doff << 2);
    if (http >= end)
    {
        decs_log_debug("empty packet");
        return;
    }
    
    ngx_atomic_fetch_add(&ngx_stat->decs_pkt_cnt, 1);
    parse_pkt(http, end, &r);
    if (r.host.data == NULL || r.uri.data == NULL)
    {
        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
                          "cant find host or uri");
        return;
    }

    r.id = (ngx_process << 24) | (decs_cfg->request_id++ & 0x00FFFFFF);
    r.iph = iph;
    
    //log begin
    decs_log_debug("req_%08xd url http://%V%V", r.id, &r.host, &r.uri);
    if (r.cookie.data)
    {
        decs_log_debug("req_%08xd cookie %V", r.id, &r.cookie);
    }
    else
    {
        decs_log_debug("req_%08xd cookie (null)", r.id);
    }
    
    if (r.ua.data)
    {
        decs_log_debug("req_%08xd user-agent %V", r.id, &r.ua);
    }
    else
    {
        decs_log_debug("req_%08xd user-agent (null)", r.id);
    }
    
    if (ngx_cycle->log->log_level >= NGX_LOG_DEBUG)
    {
        char ip[16];
        if (inet_ntop(AF_INET, &r.iph->saddr, ip, sizeof(ip)))
        {
            decs_log_debug("req_%08xd source ip %s", r.id, ip);
        }
    }
    //log end

    if (decs_cfg->decs_array->nelts > 1 && decs_load_balance(&r))
    {
        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
                          "load_balance msg error");
        return;
    }

    if (r.ua.data && decs_cfg->phone_decs_addr.sin_family)
    {
        if (ngx_strlcasestrn(r.ua.data, r.ua.data + r.ua.len, (u_char*)"iPhone", sizeof("iPhone")-2)
            || ngx_strlcasestrn(r.ua.data, r.ua.data + r.ua.len, (u_char*)"Android", sizeof("Android")-2))
        {
            sendto(decs_cfg->udp_sock, pkt, len, 0, 
                (struct sockaddr*)&decs_cfg->phone_decs_addr, sizeof(decs_cfg->phone_decs_addr));
        }
    }

    decs_process_request(&r);
    return;
}

void decs_pkt_handler_lb(char *pkt, int len)
{
    req_t     r;
    struct iphdr *iph = (struct iphdr *)(pkt + 1);
    struct tcphdr *tcph;
    u_char *end = (u_char*)pkt + len;
    u_char *http;

    if (*pkt != 0) //the first byte is reserved
    {
        decs_log_debug("unexpected pkt from other decs");
        return;
    }
    
    tcph = (struct tcphdr*)((u_char*)iph + (iph->ihl << 2));
    http = (u_char*)tcph + (tcph->doff << 2);
    if (http >= end)
    {
        decs_log_debug("empty packet");
        return;
    }
   
    parse_pkt_lb(http, end, &r);
    if (r.host.data == NULL || r.uri.data == NULL)
    {
        return;
    }
    
    r.id = (ngx_process << 24) | (decs_cfg->request_id++ & 0x00FFFFFF);
    r.iph = iph;

    //log begin
    decs_log_debug("req_%08xd url http://%V%V", r.id, &r.host, &r.uri);

    if (r.cookie.data)
    {
        decs_log_debug("req_%08xd cookie %V", r.id, &r.cookie);
    }
    else
    {
        decs_log_debug("req_%08xd cookie (null)", r.id);
    }

    if (r.ua.data)
    {
        decs_log_debug("req_%08xd user-agent %V", r.id, &r.ua);
    }
    else
    {
        decs_log_debug("req_%08xd user-agent (null)", r.id);
    }
    if (ngx_cycle->log->log_level >= NGX_LOG_DEBUG)
    {
        char ip[16];
        if (r.iph && inet_ntop(AF_INET, &r.iph->saddr, ip, sizeof(ip)))
        {
            decs_log_debug("req_%08xd source ip %s", r.id, ip);
        }
    }
    //log end

    decs_process_request(&r);
    return;
}

char *
decs_set_redis_addr(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_str_t *value;
    decs_cfg_t      *cfg = conf;
    redis_t         *r;
    
    value = cf->args->elts;

    r = &cfg->redis;
    r->redis_ip = (char*)value[1].data;
    if (cf->args->nelts >= 3)
        r->redis_port = atoi((char*)value[2].data);
    else
        r->redis_port = 6379;
    
    return NGX_CONF_OK;
}

char *
decs_set_phone_decs_addr(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_str_t *value;
    decs_cfg_t      *cfg = conf;
    decs_t          *d;
    
    value = cf->args->elts;

    memset(&d->addr_for_decs, 0, sizeof(d->addr_for_decs));
    cfg->phone_decs_addr.sin_family = AF_INET;
    cfg->phone_decs_addr.sin_addr.s_addr = inet_addr((char*)value[1].data);
    cfg->phone_decs_addr.sin_port = htons(atoi((char*)value[2].data));   

    return NGX_CONF_OK;
}

char *
decs_set_log_addr(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_str_t *value;
    decs_cfg_t      *cfg = conf;
    
    value = cf->args->elts;


	memset(&cfg->log_server_addr, 0, sizeof(cfg->log_server_addr));
    cfg->log_server_addr.sin_family = AF_INET;
    cfg->log_server_addr.sin_addr.s_addr = inet_addr((char*)value[1].data);
    cfg->log_server_addr.sin_port = htons(atoi((char*)value[2].data));

    cfg->log_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (cfg->log_sock < 0)
    {
        decs_log_error("log socket() failed, errno: %d", errno);
        return "init log socket error";
    }

    return NGX_CONF_OK;
}


char *
decs_set_decs_addr(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_str_t *value;
    decs_cfg_t      *cfg = conf;
    decs_t          *d;

    value = cf->args->elts;

    if (!cfg->decs_array)
    {
        cfg->decs_array = ngx_array_create(cf->pool, 3, sizeof(decs_t));
        if (!cfg->decs_array)
            return "ngx_array_create failed";
    }

    d = ngx_array_push(cfg->decs_array);
    if (!d) return "ngx_array_push failed";

    d->ip = (char*)value[1].data;
    d->port_for_pps = atoi((char*)value[2].data);
    if (cf->args->nelts >= 4)
    {
        d->port_for_decs = atoi((char*)value[3].data);
        memset(&d->addr_for_decs, 0, sizeof(d->addr_for_decs));
        d->addr_for_decs.sin_family = AF_INET;
        d->addr_for_decs.sin_addr.s_addr = inet_addr(d->ip);
        d->addr_for_decs.sin_port = htons(d->port_for_decs);
    }
    else
        d->port_for_decs = 0;

    if (is_local_ip(d->ip) && cfg->enabled)
    {
        if (dec_making_add_listening(cf, cfg, d) != NGX_OK)
        {
            return "dec_making_add_listening failed";
        }
    }

    return NGX_CONF_OK;
}

char *
decs_set_cookie_iframe(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_str_t *value;
    decs_cfg_t      *cfg = conf;
    FILE *fp;
    struct stat st;
    char *p;
    //int id;
    
    value = cf->args->elts;
    //id = ngx_atoi(value[1].data, value[1].len);    
    //if ((size_t)id >= sizeof(cfg->iframes) / sizeof(cfg->iframes[0])) return "id too big";

    if (0 != stat((char*)value[1].data, &st)) return "cookie file not accessable";
    p = ngx_palloc(cf->pool, st.st_size + 1);
    if (!p) return NGX_CONF_ERROR;

    fp = fopen((char*)value[1].data, "r");
    if (!fp) return "file not accessable";
    if (fread(p, st.st_size, 1, fp) != 1)
    {
        fclose(fp);
        return NGX_CONF_ERROR;
    }

    p[st.st_size] = '\0';
    cfg->cookie_iframe = p;
    fclose(fp);
    
    return NGX_CONF_OK;
}

char *
decs_set_ad_iframe(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_str_t *value;
    decs_cfg_t      *cfg = conf;
    FILE *fp;
    struct stat st;
    char *p;
    //int id;
    
    value = cf->args->elts;
    //id = ngx_atoi(value[1].data, value[1].len);
    //if ((size_t)id >= sizeof(cfg->iframes) / sizeof(cfg->iframes[0])) return "id too big";

    if (0 != stat((char*)value[1].data, &st)) return "ad file not accessable";
    p = ngx_palloc(cf->pool, st.st_size + 1);
    if (!p) return NGX_CONF_ERROR;

    fp = fopen((char*)value[1].data, "r");
    if (!fp) return "file not accessable";
    if (fread(p, st.st_size, 1, fp) != 1)
    {
        fclose(fp);
        return NGX_CONF_ERROR;
    }

    p[st.st_size] = '\0';
    cfg->ad_iframe = p;
    fclose(fp);
    
    return NGX_CONF_OK;
}

char *
decs_set_ip_rule(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    decs_cfg_t                 *cfg = conf;

    ngx_int_t                   rc;
    ngx_uint_t                  all;
    ngx_str_t                  *value;
    ngx_cidr_t                  cidr;
    decs_ip_rule_t             *rule;

    ngx_memzero(&cidr, sizeof(ngx_cidr_t));

    value = cf->args->elts;

    all = (value[1].len == 3 && ngx_strcmp(value[1].data, "all") == 0);

    if (!all) {

        rc = ngx_ptocidr(&value[1], &cidr);

        if (rc == NGX_ERROR) {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                         "invalid parameter \"%V\"", &value[1]);
            return NGX_CONF_ERROR;
        }

        if (rc == NGX_DONE) {
            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
                         "low address bits of %V are meaningless", &value[1]);
        }
    }

    if (cidr.family == AF_INET || all) {

        if (cfg->ip_rules == NULL) {
            cfg->ip_rules = ngx_array_create(cf->pool, 4,
                                           sizeof(decs_ip_rule_t));
            if (cfg->ip_rules == NULL) {
                return NGX_CONF_ERROR;
            }
        }

        rule = ngx_array_push(cfg->ip_rules);
        if (rule == NULL) {
            return NGX_CONF_ERROR;
        }

        rule->mask = cidr.u.in.mask;
        rule->addr = cidr.u.in.addr;
        rule->deny = (ngx_strncmp(value[0].data, "ip_deny", value[0].len) == 0);
    }

    return NGX_CONF_OK;
}

char *
decs_set_log_ip_rule(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    decs_cfg_t                 *cfg = conf;

    ngx_int_t                   rc;
    ngx_uint_t                  all;
    ngx_str_t                  *value;
    ngx_cidr_t                  cidr;
    decs_ip_rule_t             *rule;

    ngx_memzero(&cidr, sizeof(ngx_cidr_t));

    value = cf->args->elts;

    all = (value[1].len == 3 && ngx_strcmp(value[1].data, "all") == 0);

    if (!all) {

        rc = ngx_ptocidr(&value[1], &cidr);

        if (rc == NGX_ERROR) {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                         "invalid parameter \"%V\"", &value[1]);
            return NGX_CONF_ERROR;
        }

        if (rc == NGX_DONE) {
            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
                         "low address bits of %V are meaningless", &value[1]);
        }
    }

    if (cidr.family == AF_INET || all) {

        if (cfg->log_ip_rules == NULL) {
            cfg->log_ip_rules = ngx_array_create(cf->pool, 4,
                                           sizeof(decs_ip_rule_t));
            if (cfg->log_ip_rules == NULL) {
                return NGX_CONF_ERROR;
            }
        }

        rule = ngx_array_push(cfg->log_ip_rules);
        if (rule == NULL) {
            return NGX_CONF_ERROR;
        }

        rule->mask = cidr.u.in.mask;
        rule->addr = cidr.u.in.addr;
        rule->deny = (ngx_strncmp(value[0].data, "log_ip_deny", value[0].len) == 0);
    }

    return NGX_CONF_OK;
}

char *
decs_set_log_ext_rule(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    decs_cfg_t                 *cfg = conf;
    ngx_flag_t                  deny;
    ngx_str_t                  *value;
    
    value = cf->args->elts;

    deny = (ngx_strncmp(value[0].data, "log_ext_deny", value[0].len) == 0);
    if (cfg->log_ext_deny == NGX_CONF_UNSET)
    {
        cfg->log_ext_deny = deny;
    }
    else if (cfg->log_ext_deny != deny)
    {
        return "log_ext_allow and log_ext_deny should not be used at the same time.";
    }
    if (value[1].data[0] == '*')
    {
        cfg->log_ext_all = 1;
        return NGX_CONF_OK;
    }
    else if (cfg->log_ext_all)
    {
        return "is unnecessary";
    }
    else
    {
        return ngx_conf_set_str_array_slot(cf, cmd, conf);
    }
}

char *
decs_set_ua_rule(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    decs_cfg_t                 *cfg = conf;
    ngx_str_t                  *value;
    decs_ua_rule_t             *rule;

    value = cf->args->elts;

    if (cfg->ua_rules == NULL) {
        cfg->ua_rules = ngx_array_create(cf->pool, 4, sizeof(decs_ua_rule_t));
        if (cfg->ua_rules == NULL) {
            return NGX_CONF_ERROR;
        }
    }

    rule = ngx_array_push(cfg->ua_rules);
    if (rule == NULL) {
        return NGX_CONF_ERROR;
    }

    rule->ua = value[1];
    rule->deny = (ngx_strncmp(value[0].data, "ua_deny", value[0].len) == 0);

    return NGX_CONF_OK;
}

char *
decs_set_fixed_url_ad(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    decs_cfg_t                 *cfg = conf;
    ngx_str_t                  *value;
    ngx_hash_key_t             *url_ad;
    
    value = cf->args->elts;

    if (cfg->fixed_url_ad == NULL) {
        cfg->fixed_url_ad = ngx_array_create(cf->pool, 4, sizeof(ngx_hash_key_t));
        if (cfg->fixed_url_ad == NULL) {
            return NGX_CONF_ERROR;
        }
    }

    url_ad = ngx_array_push(cfg->fixed_url_ad);
    if (url_ad == NULL) {
        return NGX_CONF_ERROR;
    }
    if (value[1].len > sizeof("http://") - 1
        && ngx_strncmp(value[1].data, "http://", sizeof("http://") - 1) == 0)
    {
        url_ad->key.data = value[1].data + sizeof("http://") - 1;
        url_ad->key.len = value[1].data + value[1].len - url_ad->key.data;
    }
    else
    {
        url_ad->key = value[1];
    }
    url_ad->key_hash = ngx_hash_key(url_ad->key.data, url_ad->key.len);
    url_ad->value = value[2].data;
    
    return NGX_CONF_OK;
}

char *
decs_set_regex_url_ad(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    decs_cfg_t                 *cfg = conf;
    ngx_str_t                  *value;
    regex_url_ad_t             *url_ad;
    int erroffset;
    const char *error;

    value = cf->args->elts;
    
    if (cfg->regex_url_ad == NULL) {
        cfg->regex_url_ad = ngx_array_create(cf->pool, 4, sizeof(regex_url_ad_t));
        if (cfg->regex_url_ad == NULL) {
            return NGX_CONF_ERROR;
        }
    }

    url_ad = ngx_array_push(cfg->regex_url_ad);
    if (url_ad == NULL) {
        return NGX_CONF_ERROR;
    }
    if (value[1].len > sizeof("http://") - 1
        && ngx_strncmp(value[1].data, "http://", sizeof("http://") - 1) == 0)
    {
        url_ad->url_pattern.data = value[1].data + sizeof("http://") - 1;
        url_ad->url_pattern.len = value[1].data + value[1].len - url_ad->url_pattern.data;
    }
    else
    {
        url_ad->url_pattern = value[1];
    }
    url_ad->ad_id = value[2];
    url_ad->regex = pcre_compile((char*)url_ad->url_pattern.data, 0, &error, &erroffset, NULL);
    if (NULL == url_ad->regex)
    {
        decs_log_error("failed to compile url regex '%s'", url_ad->url_pattern.data);
        return NGX_CONF_ERROR;
    }

    return NGX_CONF_OK;
}

int
parse_redirect_node(ngx_conf_t *cf, xmlNodePtr node,redirect_t* direct)
{
    int erroffset;
    const char *error;
    
    if (!direct) return -1;
    memset(direct, 0, sizeof(redirect_t));
    
    for (node = node->children; node; node = node->next)
    {
        if (strcmp((char*)node->name, "target") == 0)
        {
            if (node->children->type == XML_TEXT_NODE)
            {
                direct->dst_url.data = ngx_pstrdupz(cf->pool, (char*)node->children->content);
                direct->dst_url.len = ngx_strlen(direct->dst_url.data);
            }
        }      
        
        if (strcmp((char*)node->name, "source") == 0)
        {
            if (node->children->type == XML_TEXT_NODE)
            {
                direct->re_surl = pcre_compile((char*)node->children->content, 0, &error, 
                    &erroffset, NULL);
                if (NULL == direct->re_surl)
                {
                    decs_log_error("failed to compile re_surl '%s' at line %d", node->children->content, (int)node->line);
                    return -1;
                }
            }
        }
    }

    return 0;
}

dm_t *
parse_domain_node(ngx_conf_t *cf, xmlNodePtr node)
{
    //cookie_reduce_t *cr;
    dm_t *dm;
    int erroffset;
    const char *error;
    
    dm = ngx_pcalloc(cf->pool, sizeof(dm_t));
    if (!dm) return NULL;

    memset(dm, 0, sizeof(dm_t));
    
    for (node = node->children; node; node = node->next)
    {
        if (strcmp((char*)node->name, "host") == 0)
        {
            if (node->children->type == XML_TEXT_NODE)
            {
                dm->domain.data = ngx_pstrdupz(cf->pool, (char*)node->children->content);
                dm->domain.len = ngx_strlen(dm->domain.data);
            }
        }

        if (strcmp((char*)node->name, "ifwhite") == 0)
        {
            if (node->children->type == XML_TEXT_NODE)
            {
                dm->ifwhite = atoi((char*)(node->children->content));
            }
        }        

        if (strcmp((char*)node->name, "adSpace") == 0)
        {
            if (node->children->type == XML_TEXT_NODE)
            {
                dm->space = atoi((char*)(node->children->content));
            }
        }        
        
        if (strcmp((char*)node->name, "ifexactly") == 0)
        {
            if (node->children->type == XML_TEXT_NODE)
            {
                dm->ifexactly = atoi((char*)(node->children->content));
            }
        }        
        
        if (strcmp((char*)node->name, "cookie_regex") == 0)
        {
            if (node->children->type == XML_TEXT_NODE)
            {
                dm->iscookie = 1;
                dm->re_cookie = pcre_compile((char*)node->children->content, 0, &error, 
                    &erroffset, NULL);
                if (NULL == dm->re_cookie)
                {
                    decs_log_error("failed to compile cookie_regex '%s' at line %d", node->children->content, (int)node->line);
                    return NULL;
                }
            }
        }
        if (strcmp((char*)node->name, "url_regular") == 0)
        {
            if (node->children->type == XML_TEXT_NODE)
            {
                dm->isregular = 1;
                dm->re_url_str = strdup((char*)node->children->content);
                dm->re_url = pcre_compile((char*)node->children->content, 0, &error, 
                    &erroffset, NULL);
                if (NULL == dm->re_url)
                {
                    decs_log_error("failed to compile url_reg '%s' at line %d", node->children->content, (int)node->line);
                    return NULL;
                }
            }
        }
    }

    return dm;
}

char *
decs_set_access_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    decs_cfg_t                 *cfg = conf;
    ngx_str_t                  *value;

    value = cf->args->elts;

    cfg->access_log = ngx_conf_open_file(cf->cycle, &value[1]);
    if (!cfg->access_log)
    {
        decs_log_error("failed to open file %V for write", &value[1]);
        return NGX_CONF_ERROR;
    }
    
    return NGX_CONF_OK;
}

char *
decs_set_redirect_list(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    decs_cfg_t       *cfg = conf;
    ngx_str_t        *value;
    xmlDocPtr doc;
    xmlNodePtr rootNode;
    xmlNodePtr node;
    char *name;
    redirect_t *direct;
    int rc = 0;

    value = cf->args->elts;
    doc = xmlReadFile((char*)value[1].data, "UTF-8", XML_PARSE_RECOVER|XML_PARSE_NOBLANKS); 
    if (!doc)
    {
        printf("xmlReadFile failed");
        return NGX_CONF_OK;
    }
    
    rootNode = xmlDocGetRootElement(doc);
    if (!rootNode)
    {
        xmlFreeDoc(doc);
        //return "xmlDocGetRootElement failed";
    }

    if (cfg->redirect == NULL) {
        cfg->redirect = ngx_array_create(cf->pool, 4, sizeof(redirect_t));
        if (cfg->redirect == NULL) {
            return NGX_CONF_ERROR;
        }
    }
    
    for (node = rootNode->children; node != NULL; node = node->next)
    {
        name = (char*)node->name;
        if (strcmp(name, "item") != 0) continue;
        
        direct = ngx_array_push(cfg->redirect);
        if (direct == NULL) {
            return NGX_CONF_ERROR;
        }
        
        rc = parse_redirect_node(cf, node, direct);
        if (rc == -1)
        {
            printf("parse_redirect_node failed");
        }
    }

    xmlFreeDoc(doc);
    return NGX_CONF_OK;    
}

ngx_array_t* decs_init_policy_domain_list(ngx_conf_t *cf, decs_cfg_t *cfg)
{
    ngx_array_t *keys = ngx_pcalloc(cf->pool, sizeof(ngx_array_t));

    if (ngx_array_init(keys, cf->pool, 200, sizeof(ngx_hash_key_t)) != NGX_OK)
        return NULL;

    return keys;
}


char *
decs_parse_policy_domain_list(ngx_conf_t *cf, decs_cfg_t *cfg, ngx_str_t *dm_file, ngx_array_t *keys, int policy_no)
{
    xmlDocPtr doc;
    xmlNodePtr rootNode;
    xmlNodePtr node;
    char *name;
    dm_t *dm;

    ngx_hash_key_t  *k;


    doc = xmlReadFile((char*)dm_file->data, "UTF-8", XML_PARSE_RECOVER|XML_PARSE_NOBLANKS);
    if (!doc) return "xmlReadFile failed";
    rootNode = xmlDocGetRootElement(doc);
    if (!rootNode)
    {
        xmlFreeDoc(doc);
        return "xmlDocGetRootElement failed";
    }

    for (node = rootNode->children; node != NULL; node = node->next)
    {
        name = (char*)node->name;
        if (strcmp(name, "item") != 0) continue;
        dm = parse_domain_node(cf, node);
        if (!dm)
        {
            printf("parse_domain_node failed");
        }
        else
        {
        	dm->policy = cfg->policys[policy_no];
            k = ngx_array_push(keys);
            if (!k)
            {
                xmlFreeDoc(doc);
                return "ngx_array_push failed";
            }

            k->key.data = dm->domain.data;
            k->key.len = dm->domain.len;
            k->key_hash = ngx_hash_key_lc(k->key.data, k->key.len);
            k->value = dm;
        }
    }

    xmlFreeDoc(doc);
    return NGX_CONF_OK;
}

char *
decs_save_policy_domain_list(ngx_conf_t *cf, decs_cfg_t *cfg, ngx_array_t *keys)
{
    //cookie_reduce_t *cr;
	ngx_uint_t i;
    ngx_hash_init_t  hash_init;

    hash_init.hash = &cfg->policy_domain_hash;
    hash_init.key = ngx_hash_key_lc;
    hash_init.max_size = 20000; //conf->types_hash_max_size;
    hash_init.bucket_size = 5000; //conf->types_hash_bucket_size;
    hash_init.name = "policy_domain_hash";
    hash_init.pool = cf->pool;
    hash_init.temp_pool = NULL;

    /* 先建立domain的hash, HASH出的内容是空的 */
    if (ngx_hash_empty_init(&hash_init, keys->elts, keys->nelts)
        != NGX_OK)
    {
        return NGX_CONF_ERROR;
    }

    /* 再遍历所有的domain，填写不同的策略数据 */
    for (i=0; i<keys->nelts; i++)
    {
    	dm_policy_t *dmp;
    	void **dm;
    	ngx_hash_key_t  *k = (ngx_hash_key_t*)keys->elts + i;
        dm = ngx_hash_find_ptr(&cfg->policy_domain_hash,
        	k->key_hash,
			k->key.data, k->key.len);
        /* 找到了 */
        if ((char*)*dm != NULL)
        {
        	dm_t *d = k->value;
        	dmp = ngx_pcalloc(cf->pool, sizeof(dm_policy_t));
        	if (!dmp) {return NGX_CONF_ERROR;}

        	dmp->dm = d;
        	dmp->next = (void*)(*dm);
        	dmp->weight = d->policy->weight;
        	(*dm) = (void*)dmp;
        }
        else
        {
        	return NGX_CONF_ERROR;
        }
    }

    return NGX_CONF_OK;
}

char *
decs_set_ad_policy_list(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    decs_cfg_t                 *cfg = conf;
    ngx_str_t                  *value;
    xmlDocPtr doc;
    xmlNodePtr rootNode;
    xmlNodePtr node, n;
    char *name;

    int erroffset;
    const char *error;

    ngx_array_t      *keys;
    policy_t        *policy;

    value = cf->args->elts;
    struct stat st;

    doc = xmlReadFile((char*)value[1].data, "UTF-8", XML_PARSE_RECOVER|XML_PARSE_NOBLANKS);
    if (!doc) return "xmlReadFile failed";
    rootNode = xmlDocGetRootElement(doc);
	if (!rootNode)
	{
		xmlFreeDoc(doc);
		return "xmlDocGetRootElement failed";
	}

	int policy_no = 0;
	keys = decs_init_policy_domain_list(cf, cfg);
	for (node = rootNode->children; node != NULL; node = node->next)
	{
		name = (char*)node->name;
		if (strcmp(name, "policy") != 0) continue;
		policy = ngx_pcalloc(cf->pool, sizeof(policy_t));
		if (!policy) {return "read policy not enough mem";}
		cfg->policys[policy_no] = policy;
		policy->weight = 1000;
		policy->rate = 100;

		for (n = node->children; n; n = n->next)
		 {
			 if (strcmp((char*)n->name, "name") == 0)
			 {
				 if (n->children->type == XML_TEXT_NODE)
				 {
					 policy->name.data = ngx_pstrdupz(cf->pool, (char*)n->children->content);
					 policy->name.len = ngx_strlen(policy->name.data);
				 }
			 }

			 if (strcmp((char*)n->name, "weight") == 0)
			 {
				 if (n->children->type == XML_TEXT_NODE)
				 {
					 policy->weight = ngx_atoi((u_char*)n->children->content, strlen((char*)n->children->content));
				 }
			 }

			 if (strcmp((char*)n->name, "rate") == 0)
			 {
				 if (n->children->type == XML_TEXT_NODE)
				 {
					 policy->rate = ngx_atoi((u_char*)n->children->content, strlen((char*)n->children->content));
				 }
			 }
			 if (strcmp((char*)n->name, "per_ip_policy") == 0)
			 {
				 if (n->children->type == XML_TEXT_NODE)
				 {
					 policy->per_ip_policy = ngx_atoi((u_char*)n->children->content, strlen((char*)n->children->content));
				 }
			 }
			 if (strcmp((char*)n->name, "per_ip_policy_dm") == 0)
			 {
				 if (n->children->type == XML_TEXT_NODE)
				 {
					 policy->per_ip_policy_dm = ngx_atoi((u_char*)n->children->content, strlen((char*)n->children->content));
				 }
			 }

			 if (strcmp((char*)n->name, "domain") == 0)
			 {
				 if (n->children->type == XML_TEXT_NODE)
				 {
					 policy->dm_file.data = ngx_pstrdupz(cf->pool, (char*)n->children->content);
					 policy->dm_file.len = ngx_strlen(policy->dm_file.data);
					 if (NGX_CONF_OK != decs_parse_policy_domain_list(cf, cfg, &policy->dm_file, keys, policy_no))
					 {
						 return "read domain list fail";
					 }
				 }
			 }

			 if (strcmp((char*)n->name, "template") == 0)
			 {
				 if (n->children->type == XML_TEXT_NODE)
				 {
					 char *p;
					 FILE *fp;
					 policy->template.data = ngx_pstrdupz(cf->pool, (char*)n->children->content);
					 policy->template.len = ngx_strlen(policy->template.data);

					    if (0 != stat((char*)policy->template.data, &st)) return "template not accessable";
					    p = ngx_palloc(cf->pool, st.st_size + 1);
					    if (!p) return NGX_CONF_ERROR;

					    fp = fopen((char*)policy->template.data, "r");
					    if (!fp) return "template not accessable";
					    if (fread(p, st.st_size, 1, fp) != 1)
					    {
					        fclose(fp);
					        return NGX_CONF_ERROR;
					    }

					    p[st.st_size] = '\0';
					    policy->templatedata = p;
				 }
			 }
			 if (strcmp((char*)n->name, "redirect") == 0)
			 {
				 if (n->children->type == XML_TEXT_NODE)
				 {
					 policy->redirect_url.data = ngx_pstrdupz(cf->pool, (char*)n->children->content);
					 policy->redirect_url.len = ngx_strlen(policy->redirect_url.data);
				 }
			 }
			 if (strcmp((char*)n->name, "clientip") == 0)
			 {
				 if (n->children->type == XML_TEXT_NODE)
				 {
					 policy->clientip.data = ngx_pstrdupz(cf->pool, (char*)n->children->content);
					 policy->clientip.len = ngx_strlen(policy->clientip.data);
				 }
			 }
			 if (strcmp((char*)n->name, "ua") == 0)
			 {
				 if (n->children->type == XML_TEXT_NODE)
				 {
					 policy->re_ua = pcre_compile((char*)n->children->content, 0, &error,
							 &erroffset, NULL);
					 if (NULL == policy->re_ua)
					 {
						 decs_log_error("failed to compile cookie_regex '%s' at line %d", n->children->content, (int)n->line);
						 return "fail to compile cookie_regex";
					 }
				 }
			 }
		 }

		policy_no++;
	}
	cfg->policy_num = policy_no;
	decs_save_policy_domain_list(cf, cfg, keys);
	xmlFreeDoc(doc);
	return NGX_CONF_OK;
}

char *
decs_set_domain_list(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    decs_cfg_t                 *cfg = conf;
    ngx_str_t                  *value;
    xmlDocPtr doc;
    xmlNodePtr rootNode;
    xmlNodePtr node;
    char *name;
    dm_t *dm;
    //cookie_reduce_t *cr;
    ngx_hash_init_t  hash_init;
    ngx_array_t      keys;
    ngx_hash_key_t  *k;
    
    value = cf->args->elts;

    if (ngx_array_init(&keys, cf->pool, 200, sizeof(ngx_hash_key_t)) != NGX_OK)
        return "ngx_array_init failed";
    
    doc = xmlReadFile((char*)value[1].data, "UTF-8", XML_PARSE_RECOVER|XML_PARSE_NOBLANKS); 
    if (!doc) return "xmlReadFile failed";
    rootNode = xmlDocGetRootElement(doc);
    if (!rootNode)
    {
        xmlFreeDoc(doc);
        return "xmlDocGetRootElement failed";
    }
    
    for (node = rootNode->children; node != NULL; node = node->next)
    {
        name = (char*)node->name;
        if (strcmp(name, "item") != 0) continue;
        dm = parse_domain_node(cf, node);
        if (!dm)
        {
            printf("parse_domain_node failed");
        }
        else
        {
            k = ngx_array_push(&keys);
            if (!k)
            {
                xmlFreeDoc(doc);
                return "ngx_array_push failed";
            }
            k->key = dm->domain;
            k->key_hash = ngx_hash_key_lc(k->key.data, k->key.len);
            k->value = dm;
        }
    }

    hash_init.hash = &cfg->cookie_reduce_hash;
    hash_init.key = ngx_hash_key_lc;
    hash_init.max_size = 20000; //conf->types_hash_max_size;
    hash_init.bucket_size = 5000; //conf->types_hash_bucket_size;
    hash_init.name = "cookie_reduce_hash";
    hash_init.pool = cf->pool;
    hash_init.temp_pool = NULL;
    if (ngx_hash_init(&hash_init, keys.elts, keys.nelts)
        != NGX_OK)
    {
        return NGX_CONF_ERROR;
    }

    xmlFreeDoc(doc);
    return NGX_CONF_OK;
}



static ngx_int_t
decs_init_shared_mem(ngx_shm_zone_t *shm_zone, void *data)
{
    decs_cfg_t             *old_cfg = data;
    decs_cfg_t             *new_cfg;

    size_t                  len;

    new_cfg = shm_zone->data;

    if (old_cfg) {
        new_cfg->sh = new_cfg->sh;

        new_cfg->shpool = old_cfg->shpool;

        return NGX_OK;
    }

    new_cfg->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;

    if (shm_zone->shm.exists) {
        new_cfg->sh = new_cfg->shpool->data;
        return NGX_OK;
    }

    new_cfg->sh = ngx_slab_alloc(new_cfg->shpool, sizeof(decs_rbtree_sh_t));
    if (new_cfg->sh == NULL) {
        return NGX_ERROR;
    }

    new_cfg->shpool->data = new_cfg->sh;

    ngx_rbtree_init(&new_cfg->sh->rbtree, &new_cfg->sh->sentinel,
                    decs_rbtree_insert_value);

    len = sizeof(" in cache keys zone \"\"") + shm_zone->shm.name.len;

    new_cfg->shpool->log_ctx = ngx_slab_alloc(new_cfg->shpool, len);
    if (new_cfg->shpool->log_ctx == NULL) {
        return NGX_ERROR;
    }

    ngx_sprintf(new_cfg->shpool->log_ctx, " in cache keys zone \"%V\"%Z",
                &shm_zone->shm.name);

    return NGX_OK;
}

char *
decs_set_shared_mem(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    decs_cfg_t                 *cfg = conf;
    ngx_str_t  *value;
    size_t           size;

    value = cf->args->elts;

    if (ngx_strcmp(value[1].data, "off") == 0) {
        return NGX_CONF_OK;
    }

    size = ngx_parse_size(&value[2]);
    if (size == (size_t) NGX_ERROR) {
        return "invalid value";
    }

    cfg->shm_zone = ngx_shared_memory_add(cf, &value[1], size, NULL);
    if (cfg->shm_zone == NULL) {
        return NGX_CONF_ERROR;
    }
    cfg->shm_zone->init = decs_init_shared_mem;
    cfg->shm_zone->data = cfg;

    return NGX_CONF_OK;
}


void *decs_create_conf(ngx_cycle_t *cycle)
{
    decs_cfg_t *cfg;
    
    cfg = ngx_pcalloc(cycle->pool, sizeof(decs_cfg_t));
    if (!cfg) return NULL;

    cfg->pool = cycle->pool;
    cfg->url_ext = NGX_CONF_UNSET_PTR;
    cfg->cookie_expire = NGX_CONF_UNSET;
    cfg->cookie_map_interval = NGX_CONF_UNSET;
    cfg->cfg_expire = NGX_CONF_UNSET;
    cfg->user_ad_expire = NGX_CONF_UNSET;
    cfg->user_ad_interval = NGX_CONF_UNSET;
    cfg->cfg_sync_interval = NGX_CONF_UNSET;
    cfg->sync_delay = NGX_CONF_UNSET;
    cfg->httplog_enabled = NGX_CONF_UNSET;
    cfg->log_ext = NGX_CONF_UNSET_PTR;
    cfg->log_ext_deny = NGX_CONF_UNSET;
    cfg->log_disk_num = NGX_CONF_UNSET;
    cfg->log_domain = NGX_CONF_UNSET_PTR;
    cfg->ad_enabled = NGX_CONF_UNSET;
    cfg->cookie_mapping_enabled = NGX_CONF_UNSET;
    cfg->show_rate = NGX_CONF_UNSET;
    cfg->click_rate = NGX_CONF_UNSET;
    
    return cfg;
}

char *decs_init_conf(ngx_cycle_t *cycle, void *conf)
{
    decs_cfg_t *cfg = conf;
    int    i;
    ngx_str_t path;
    
    ngx_conf_init_value(cfg->cookie_expire, 86400);
    ngx_conf_init_value(cfg->cookie_map_interval, 86400);
    ngx_conf_init_value(cfg->cfg_expire, 1800);     //half an hour
    ngx_conf_init_value(cfg->cfg_sync_interval, 60);
    ngx_conf_init_value(cfg->sync_delay, 10);
    ngx_conf_init_value(cfg->user_ad_expire, 86400);
    ngx_conf_init_value(cfg->user_ad_interval, 3600); 
    
    ngx_conf_init_ptr_value(cfg->url_ext, NULL);
    ngx_conf_init_value(cfg->httplog_enabled, 0);
    ngx_conf_init_ptr_value(cfg->log_ip_rules, NULL);
    ngx_conf_init_ptr_value(cfg->log_ext, NULL);
    ngx_conf_init_value(cfg->log_ext_deny, 1); //Ĭ�Ϻ�����
    ngx_conf_init_value(cfg->log_disk_num, 1);
    ngx_conf_init_ptr_value(cfg->log_domain, NULL);
    ngx_conf_init_value(cfg->ad_enabled, 1);
    ngx_conf_init_value(cfg->cookie_mapping_enabled, 1);
    ngx_conf_init_value(cfg->show_rate, 0);
    ngx_conf_init_value(cfg->click_rate, 0);
    
    if (cfg->redis.redis_ip == NULL)
    {
        return "redis not configured.";
    }
    if (cfg->area.data == NULL)
    {
        ngx_str_set(&cfg->area, "null");
    }
    if (cfg->log_disk_num < 1)
    {
        return "invalid log_disk_num";
    }

    if (cfg->fixed_url_ad)
    {
        ngx_hash_init_t  hash_init;
        
        hash_init.hash = &cfg->fixed_url_ad_hash;
        hash_init.key = ngx_hash_key_lc;
        hash_init.max_size = 20000; //conf->types_hash_max_size;
        hash_init.bucket_size = 5000; //conf->types_hash_bucket_size;
        hash_init.name = "fixed_url_ad_hash";
        hash_init.pool = cycle->pool;
        hash_init.temp_pool = NULL;
        if (ngx_hash_init(&hash_init, cfg->fixed_url_ad->elts, cfg->fixed_url_ad->nelts)
            != NGX_OK)
        {
            return NGX_CONF_ERROR;
        }
    }
    
    if (cfg->httplog_enabled)
    {
        cfg->log_files = ngx_palloc(cycle->pool, cfg->log_disk_num * sizeof(ngx_open_file_t*));
        if (!cfg->log_files)
            return "ngx_palloc failed";
        for (i = 0; i < (int)cfg->log_disk_num; i++)
        {
            path.data = ngx_pnalloc(cycle->pool, cfg->log_dir.len + 30);
            if (!path.data)
            {
                return "ngx_pnalloc failed";
                
            }
            path.len = ngx_sprintf(path.data, "%V/%02d/http_log", &cfg->log_dir, i) - path.data;
            path.data[path.len] = '\0';
            cfg->log_files[i] = ngx_conf_open_file(cycle, &path);
            if (!cfg->log_files[i])
            {
                decs_log_error("failed to open log file %V", &path);
                return "failed to open log file";
            }
        }

        if (cfg->log_domain)
        {
            ngx_hash_init_t  hash_init;
            ngx_array_t      keys;
            ngx_hash_key_t  *k;
            ngx_str_t       *dm;
            
            if (ngx_array_init(&keys, cycle->pool, 200, sizeof(ngx_hash_key_t)) != NGX_OK)
                return "ngx_array_init failed";

            for (i = 0; i < (int)cfg->log_domain->nelts; i++)
            {
                dm = &((ngx_str_t*)cfg->log_domain->elts)[i];
                k = ngx_array_push(&keys);
                if (!k)
                {
                    return "ngx_array_push failed";
                }
                k->key = *dm;
                k->key_hash = ngx_hash_key_lc(k->key.data, k->key.len);
                k->value = dm;
            }
            
            hash_init.hash = &cfg->log_domain_hash;
            hash_init.key = ngx_hash_key_lc;
            hash_init.max_size = 20000; //conf->types_hash_max_size;
            hash_init.bucket_size = 5000; //conf->types_hash_bucket_size;
            hash_init.name = "log_domain_hash";
            hash_init.pool = cycle->pool;
            hash_init.temp_pool = NULL;
            if (ngx_hash_init(&hash_init, keys.elts, keys.nelts)
                != NGX_OK)
            {
                return NGX_CONF_ERROR;
            }
        }
    }
    
    return NGX_CONF_OK;
}

ngx_int_t 
decs_init_module(ngx_cycle_t *cycle, decs_cfg_t *cfg)
{
    struct mq_attr attr;
    int    n;
    int    i;

    if (cfg->httplog_enabled)
    {
        for (i = 0; i < cfg->log_disk_num; i++)
        {
            if (ngx_nonblocking(cfg->log_files[i]->fd) != 0)
            {
                decs_log_error("ngx_nonblocking failed on log file, errno %d ", errno);
            }
        }
    }
    
    cfg->raw_sock = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
    if (cfg->raw_sock < 0)
    {
        decs_log_error("socket() failed, errno: %d", errno);
        return NGX_ERROR;
    }


    n = 1;
    if (setsockopt(cfg->raw_sock, IPPROTO_IP, IP_HDRINCL, &n, sizeof(n)) < 0)
    {
        decs_log_error("setsockopt() failed, errno: %d", errno);
        return NGX_ERROR;
    }

    cfg->udp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (cfg->udp_sock < 0)
    {
        decs_log_error("socket() failed, errno: %d", errno);
        return NGX_ERROR;
    }

#if 0
    /*ͬ��socket*/
    cfg->sync_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(cfg->sync_sock < 0)
    {
        decs_log_error("sync socket() failed, errno: %d", errno);
        return NGX_ERROR;
    }
#endif

#if 0
    if (mq_unlink("/decs_sync_queue") == -1 && errno != ENOENT)
    {
        decs_log_error("failed to delete /decs_sync_queue, errno: %d", errno);
    }

    memset(&attr, 0, sizeof(attr));
    attr.mq_flags = O_NONBLOCK;
    attr.mq_msgsize = sizeof(sync_para_t);
    attr.mq_maxmsg = 1000;
    for (i = 0; i < 10; i++) //���������������Ͻ���δ��ȫ�˳��������޷����´�������
    {
        cfg->mqd_send = mq_open("/decs_sync_queue", O_WRONLY | O_NONBLOCK | O_CREAT, 0, &attr);
        if (cfg->mqd_send == -1)
        {
            decs_log_debug("failed to open /decs_sync_queue for write, errno: %d", errno);
            sleep(1);
        }
        else
        {
            decs_log_debug("success in open /decs_sync_queue for write");
            break;
        }
    }
    if (i == 10)
    {
        decs_log_error("failed to open /decs_sync_queue for write, errno: %d", errno);
        return NGX_ERROR;
    }
#endif

    cfg->mqd_send = mq_open("/decs_sync_queue", O_WRONLY | O_NONBLOCK, 0, NULL);
    if (cfg->mqd_send == -1)
    {
        if (errno != ENOENT)
        {
            decs_log_error("failed to open /decs_sync_queue for write, errno: %d", errno);
            return NGX_ERROR;
        }
        memset(&attr, 0, sizeof(attr));
        attr.mq_flags = O_NONBLOCK;
        attr.mq_msgsize = sizeof(sync_para_t);
        attr.mq_maxmsg = 1000;            
        cfg->mqd_send = mq_open("/decs_sync_queue", O_WRONLY | O_NONBLOCK | O_CREAT, 0, &attr);
        if (cfg->mqd_send == -1)
        {
            decs_log_error("failed to open /decs_sync_queue for write, errno: %d", errno);
            return NGX_ERROR;
        }
    }
    
    cfg->mqd_recv = mq_open("/decs_sync_queue", O_RDONLY, 0, NULL);
    if (cfg->mqd_recv == -1)
    {
        decs_log_error("failed to open /decs_sync_queue for read, errno: %d", errno);
        return NGX_ERROR;
    }

    return NGX_OK;
}

